package com.cc.stream;

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author C.
 * @Date 2019-03-31 18:41
 * <p>
 * Stream并不是我们以前认识的IO流，而是一个数据渠道，
 * 用于操作数据源（集合、数组等）所生成的元素序列。
 * 集合专注的是数据，流专注的是计算。
 */
public class StreamTest {

    /**
     * 使用 Stream 操作有三个步骤
     * 1.创建 Stream：从一个数据源（如集合、数组）中获取一个流
     * 2.中间操作：一个或多个中间操作，对数据源的数据进行处理
     * 3.终止操作：执行中间操作链，并产生结果
     */

    /**
     * 1-10个数作为集合的元素，然后分别通过传统迭代方式和Stream方式把大于5的数字打印出来。
     */
    @Test
    public void test01() {
        List<Integer> list = Arrays.asList(2, 3, 5, 4, 1, 8, 10, 9, 7, 6);

        // 传统方式
        for (Integer num : list) {
            if (num > 5)
                System.out.println(num);
        }

        System.out.println("-------------");

        // Stream方式
        list.stream()//
                .filter((e) -> e > 5)//
                .forEach(System.out::println);
    }

    /**
     * 创建 Stream
     * 由集合创建 Stream
     * Java8 中的 Collection 接口被扩展了，提供了两个获取流的方法：
     * default Stream stream() : 返回一个顺序流
     * default Stream parallelStream() : 返回一个并行流
     */
    @Test
    public void test_for_create_stream_from_list() {
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream(); //获取一个顺序流
        Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
        System.out.println(stream);
        System.out.println(parallelStream);
    }

    /**
     * 由数组创建流
     * Java8 中的 Arrays 的静态方法 stream() 可以从数组获取流：
     * static Stream stream(T[] array): 返回一个流
     * 还有重载形式，能够处理对应基本类型的数组：
     * public static IntStream stream(int[] array)
     * public static LongStream stream(long[] array)
     * public static DoubleStream stream(double[] array)
     */
    @Test
    public void test_for_create_stream_from_array() {
        Integer[] nums = new Integer[8];
        Stream<Integer> stream = Arrays.stream(nums);
        System.out.println(stream);
    }

    /**
     * 由值创建流
     * 可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
     * public static Stream of(T… values) : 返回一个流
     */
    @Test
    public void test_for_create_stream_from_values() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
        System.out.println(stream);
    }

    /**
     * 由函数创建流
     * 可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。
     * public static Stream iterate(final T seed, final UnaryOperator f)
     * public static Stream generate(Supplier s) :
     * 所谓无限流，顾名思义就是无限的.
     */
    @Test
    public void test_for_create_stream_from_iterate() {
        Stream.iterate(1, (x) -> x + 1).forEach(System.out::println);
    }

    @Test
    public void test_for_create_stream_from_generate() {
        Stream.generate(Math::random).forEach(System.out::println);
    }

    /**
     * 中间操作
     * 中间操作又分四类
     *      筛选
     *      切割
     *      映射
     *      排序
     */

    /**
     * 筛选
     * 筛选操作有两个方法
     * filter(Predicate p)， 接收Predicate实例，根据该实例的test方法进行筛选
     * <p>
     * 用Lambda表达式创建Predicate实例，其中(e) -> e > 5便是test方法的实现，
     * 具体为接收一个Integer类型参数e，该参数如果大于5则test方法返回true，否则false。
     */
    @Test
    public void test_filter() {
        List<Integer> list = Arrays.asList(2, 3, 5, 4, 1, 8, 10, 9, 7, 6);
        list.stream()//
                .filter((e) -> e > 5)//
                .forEach(System.out::println);
    }

    /**
     * distinct()，这就是去重复的，根据流所生成元素的 hashCode() 和 equals() 去除重复元素
     */
    @Test
    public void test_distinct() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);
        list.stream()//
                .distinct()//
                .forEach(System.out::println);
    }

    /**
     * 切割也有两个方法
     * limit(long maxSize)，截断流，使其元素不超过给定数量，跟SQL语句很像。
     */
    @Test
    public void test_limit() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        list.stream()//
                .limit(3)//
                .forEach(System.out::println);
    }

    /**
     * skip(long n) ，返回一个去掉了前 n 个元素的流。若流中元素不足 n 个，则返回一个空流。
     * limit方法互补了，limit去尾，skip去头
     */
    @Test
    public void test_skip() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        list.stream()//
                .skip(3)//
                .forEach(System.out::println);
    }

    /**
     * 映射
     * map(Function f) ，接收一个Function实例，
     * Function的抽象方法apply是接收一个参数然后返回一个值，这个返回值就是映射的结果。
     * <p>
     * 流中的每个元素映射出来的新值是该元素的平方
     */
    @Test
    public void test_map() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream()//
                .map(x -> x * x)
                .forEach(System.out::println);
    }

    /**
     * mapToDouble(ToDoubleFunction f)，这个函数跟map很像，只不过映射的结果一定是要Double类型。
     * 类似的还有mapToInt(ToIntFunction f)和mapToLong(ToLongFunction f)方法
     */
    @Test
    public void test_mapToDouble() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream()//
                .mapToDouble(x -> x + 0.1)
                .forEach(System.out::println);
    }

    /**
     * flatMap(Function f)，将流中的每个元素映射成一个流
     */
    @Test
    public void test_flatMap() {
        List<String> list = Arrays.asList("abc", "efg", "xyz");
        list.stream()//
                .flatMap(StreamTest::string2Stream)//
                .forEach(System.out::println);
    }

    /**
     * 接收一个字符串将其所以的字符添加到list中然后返回stream
     *
     * @param str
     * @return
     */
    public static Stream<Character> string2Stream(String str) {
        List<Character> list = new ArrayList<>();
        char[] charArray = str.toCharArray();
        for (char c : charArray) {
            list.add(c);
        }
        return list.stream();
    }

    /**
     * 排序
     * sorted()，可以将流中元素按自然顺序排序
     */
    @Test
    public void test_sorted() {
        List<String> list = Arrays.asList("d", "a", "c");
        list.stream()//
                .sorted()//
                .forEach(System.out::println);
    }

    /**
     * sorted(Comparator comp)，可以将流中元素按比较器顺序排序。
     */
    @Test
    public void test_sorted_02() {
        List<String> list = Arrays.asList("d", "a", "c");
        list.stream()//
                .sorted((x, y) -> -x.compareTo(y))//
                .forEach(System.out::println);
    }


    /**
     * 终止操作
     * 终止操作会从流的中间操作流水线生成结果。
     * 其结果可以是任何不是流的值，例如： List、 Integer，甚至是 void 。
     *
     * 其中终止操作又有三类
     *      查找与匹配
     *      归约
     *      收集
     */

    /**
     * 查找与匹配
     * 查找与匹配有下面这么多方法，
     * allMatch(Predicate p) 检查是否匹配所有元素
     * anyMatch(Predicate p) 检查是否至少匹配一个元素
     * noneMatch(Predicate p) 检查是否没有匹配所有元素
     * findFirst() 返回第一个元素
     * findAny() 返回当前流中的任意元素
     * count() 返回流中元素总数
     * max(Comparator c) 返回流中最大值
     * min(Comparator c) 返回流中最小值
     * forEach(Consumer c) 内部迭代 (使用 Collection 接口需要用户去做迭代，称为外部迭代。相反， Stream API 使用内部迭代)
     */

    /**
     * allMatch(Predicate p)
     */
    @Test
    public void test_allMatch() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        boolean allMatch = list.stream()//
                .allMatch(x -> x > 2);//是否全部元素都大于2
        System.out.println(allMatch);
    }


    /**
     * findFirst()
     */
    @Test
    public void test_findFirst() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        Optional<Integer> first = list.stream()//
                .findFirst();
        Integer val = first.get();
        System.out.println(val);//输出10
    }

    /**
     * max(Comparator c)
     */
    @Test
    public void test_max() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        Optional<Integer> first = list.stream()//
                .max(Integer::compareTo);
        Integer val = first.get();
        System.out.println(val);//输出3
    }

    /**
     * 归约
     * 归约操作有下面两个方法
     *      reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来，得到一个值。返回 T
     *      reduce(BinaryOperator b) 可以将流中元素反复结合起来，得到一个值。返回 Optional
     */

    /**
     * reduce(T iden, BinaryOperator b)
     */
    @Test
    public void test_reduce() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        Integer result = list.stream()//
                .reduce(2, Integer::sum);
        System.out.println(result);//输出27，其实相当于2+10+5+7+3，就是一个累加
    }

    /**
     * reduce(BinaryOperator b)
     */
    @Test
    public void test_reduce_02() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        Optional<Integer> optional = list.stream()//
                .reduce(Integer::sum);
        Integer result = optional.get();
        System.out.println(result);//输出25，其实相当于10+5+7+3，就是一个累加
    }

    /**
     * 收集
     * collect(Collector c) 将流转换为其他形式。接收一个Collector接口的
     * 实现，用于给流中元素做汇总的方法。
     */
    @Test
    public void test_collect() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);
        // 将流中元素收集到List中
        List<Integer> resultList = list.stream()//
                .collect(Collectors.toList());
        // 将流中元素收集到Set中
        Set<Integer> resultSet = list.stream()//
                .collect(Collectors.toSet());
        System.out.println(resultList);// 输出[10, 5, 7, 3]
        System.out.println(resultSet);// 输出[3, 5, 7, 10]
    }

    /**
     * 上面的代码分别将流中的元素收集到List和Set中，
     * 其实还可以收集到其类型中，如Map、Optional甚至是Integer等。
     * 在收集过程中还可以做点其它操作，比如下面例子
     */
    @Test
    public void test() {
        List<Integer> list = Arrays.asList(10, 5, 7, 3);

        //计算出流中的元素个数
        Long count = list.stream()//
                .collect(Collectors.counting());
        System.out.println(count);//输出4

        //计算流中Integer类型数据的总和
        Integer sum = list.stream()//
                .collect(Collectors.summingInt(x -> x));
        System.out.println(sum);//输出25
    }

    /**
     * Stream 延迟执行
     * Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
     */
    /**
     * 运行这个测试方法，你会发现没有输出任何东西，因为它并没有终止操作，所以中间操作并未执行。下面加多一个终止操作
     */
    @Test
    public void test_01() {
        List<Person> list = Arrays.asList(//
                new Person("Jason", 18), //
                new Person("Hank", 46), //
                new Person("Alice", 23));
        // 过滤操作，找出age大于20的Person对象，但是没有终止操作
        list.stream()//
                .filter(x -> x.getAge() > 20);
    }

    /**
     * getAge()方法执行了，而且查找出两个age大于 20 的Person对象并输出
     */
    @Test
    public void test_02() {
        List<Person> list = Arrays.asList(//
                new Person("Jason", 18), //
                new Person("Hank", 46), //
                new Person("Alice", 23));
        // 过滤操作，找出age大于20的Person对象，但是没有终止操作
        list.stream()//
                .filter(x -> x.getAge() > 20)//
                .forEach(System.out::println);
    }


    /**
     * Stream 不会改变源对象。相反，他们会返回一个持有结果的新Stream（这样可以链式编程）
     */
    @Test
    public void test_03() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        list.stream()//
                .filter(x -> x > 3)// 筛选出大于3的元素
                .forEach(System.out::println);

        System.out.println(list);
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        System.out.println("getAge()执行");
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}